package cn.ilovese.collect.serviceimpl.recommend;

import cn.ilovese.collect.datamapper.coordination.CoordinationAbilityMapper;
import cn.ilovese.collect.datamapper.coordination.CoordinationRankingMapper;
import cn.ilovese.collect.datamapper.follow.FollowMapper;
import cn.ilovese.collect.datamapper.report.*;
import cn.ilovese.collect.datamapper.task.TaskMapper;
import cn.ilovese.collect.datamapper.user.ActivityMapper;
import cn.ilovese.collect.datamapper.user.UserMapper;
import cn.ilovese.collect.dto.recommend.PCA;
import cn.ilovese.collect.dto.recommend.WorkerGroupRecommend;
import cn.ilovese.collect.dto.similarity.MySimHash;
import cn.ilovese.collect.po.coordination.CoordinationAbility;
import cn.ilovese.collect.po.coordination.CoordinationRanking;
import cn.ilovese.collect.po.coordination.ReportTask;
import cn.ilovese.collect.po.report.*;
import cn.ilovese.collect.po.task.Task;
import cn.ilovese.collect.po.user.Activity;
import cn.ilovese.collect.po.user.User;
import cn.ilovese.collect.service.recommend.GroupRecommendService;
import cn.ilovese.collect.util.Constant;
import cn.ilovese.collect.util.MyDateUtil;
import cn.ilovese.collect.vo.Coordination.CoordinationAbilityVO;
import cn.ilovese.collect.vo.Coordination.WorkerSimilarityVO;
import cn.ilovese.collect.vo.ResultVO;
import cn.ilovese.collect.vo.similarity.ReportSimilarityVO;
import cn.ilovese.collect.vo.task.TaskRecommendVO;
import cn.ilovese.collect.vo.task.taskRecommendWorkersVO;
import cn.ilovese.collect.vo.user.UserVO;
import com.hankcs.hanlp.HanLP;
import com.hankcs.hanlp.corpus.tag.Nature;
import com.hankcs.hanlp.seg.common.Term;
import javafx.util.Pair;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class GroupRecommendServiceImpl implements GroupRecommendService {
    private static WorkerGroupRecommend instance;

    @Resource
    UserMapper userMapper;
    @Resource
    ScoreMapper scoreMapper;
    @Resource
    TFIDFSimilarityMapper tfidfSimilarityMapper;
    @Resource
    TaskMapper taskMapper;
    @Resource
    ReportMapper reportMapper;
    @Resource
    ActivityMapper activityMapper;
    @Resource
    BossScoreMapper bossScoreMapper;
    @Resource
    FollowMapper followMapper;
    @Resource
    LikeMapper likeMapper;
    @Resource
    ScoreSimilarityMapper scoreSimilarityMapper;
    @Resource
    CoordinationAbilityMapper coordinationAbilityMapper;
    @Resource
    CoordinationRankingMapper coordinationRankingMapper;

    //TODO:群体为空（染色体全为false）的时候特判，不能选这种染色体！！
    //TODO:ly jyg也写了一些工人特性相关的东西，包括PPT给的专业能力、任务偏好、活跃度、测试设备，都可以加入优化目标。
    //TODO:目前是只有两个指标的！先把框架搞出来吧..
    //TODO:添加两个“领域知识”（和任务相关性+工人之间区分性） and 修改这个workerScore用的数据
    private static List<Double> bugDetectChance;//按顺序，每个工人有多大几率能检测出bug，用bossScoresAndGeneralScores和活跃度一起算出来
    private static List<Double> workerScore;//工人的一个综合能力打分，用最下面四个指标算出来
    private static List<Integer> workerId;
    private static List<Double> workerRelevance;//领域相似度
    private static List<List<Double>> workerDiversity;//工人多样性
    private static Map<Integer,List<String>> workerDomains;//所有工人的技术领域
    private static List<String> taskDomain;//本任务的技术领域
    private static List<List<Double>> bossScoresAndGeneralScores;//boss五个打分的平均得分+报告平均得分
    private static List<Double> activeness;//活跃度，被归一化到0-5之间（方便和要一起用的几个统一）
    private static List<Double> followers;  //关注者数目(归一化到0-5之间)
    private static List<Double> reportDup;  //报告重复度(归一化到0-5之间)
    private static List<Double> commentDup;  //评论重复度(归一化到0-5之间)
    private static List<Double> likes;  //评论被点赞数目(归一化到0-5之间)

    //每一代扩增到100个 再选出50个进入下一代
    private static int groupSizeBefore=100;
    private static int groupSizeAfter=50;

    private static MySimHash hashTask;
    private static List<MySimHash> hashWorker;


    List<Integer> recommendExecute(Integer taskId) {
        //一些初始化
        //小规模的时候！！（只worker总人数就不多）
        List<User> workers=userMapper.selectByRole("worker");
        if(workers.size()<4){
            List<Integer>res=new ArrayList<>();
            for(int i=0;i<workers.size();i++)
                res.add(workers.get(i).getId());
            return res;
        }
        if(workers.size()<20){
            //数据分治；用动态规划处理工人人数更少的情况。
            //工业环境不会太去考虑这种人数场景，但有几点思考：
            /*
             * 1.遗传算法在人数较少的时候完全是低效且没必要的，精度也会相对低一些
             * 2.网站会有一些众包工人人数较少的场景出现，例如创办初期；或延展业务，包给某小规模团队做内部众包测试
             * 3.为小规模情况
             * */
        }
        workerId=new ArrayList<>();
        bugDetectChance=new ArrayList<>();
        workerScore=new ArrayList<>();
        workerRelevance=new ArrayList<>();
        workerDomains=new HashMap<>();
        taskDomain=getTaskDomainKnowledge(taskId);
        List<List<Boolean>> workerSets=new ArrayList<>();//遗传算法的染色体；选择工人的集合
        for(User worker:workers){
            workerId.add(worker.getId());
            List<String> domain=getWorkerReportDomainKnowledge(worker.getId());
            if(worker.getFreqDevice()!=null)
                domain.add(worker.getFreqDevice());
            if(worker.getActivity()!=null)
                domain.add(worker.getActivity());
            if(worker.getSkill()!=null)
                domain.add(worker.getSkill());
            workerDomains.put(worker.getId(),domain);
            //workerRelevance.add(Math.random());
        }
        //初步计算几大指标
        initIndexs(workers);

        //初始化种群：固定大小size
        //System.out.println(workerDomains);
        while (workerSets.size()<groupSizeBefore){
            List<Boolean>now=new ArrayList<>();
            for(int j=0;j<workers.size();j++){
                //随机初始化 多大概率选择可调整
                double number = Math.random();
                if(number>0.3){
                    now.add(false);
                }
                else{
                    now.add(true);
                }
            }
            if(calcWorkerNum(now)==0)continue;
            workerSets.add(now);
        }
        List<Integer>res=NSGA_GetBestSet(workerSets);
        return res;
    }
    //遗传算法主方法
    List<Integer> NSGA_GetBestSet(List<List<Boolean>> workerSets){
        List<Integer>res=new ArrayList<>();
        //TODO:遗传算法迭代原则：迭代100次？
        int T=100;
        if(workerSets.get(0).size()>15)T=50;
        if(workerSets.get(0).size()>50)T=30;
        List<List<Boolean>> workerSetsNow=workerSets;
        while(T>0){
            //System.err.println(T);
            T--;
            //System.out.println(workerSetsNow);
            workerSetsNow=processNSGA(workerSetsNow);
        }
        Double resNum=Double.MAX_VALUE;
        Integer resID=0;
        //System.out.println(workerSetsNow);
        //对workerSet进行操作，仅保留其pareto最优解，然后再做最后一步的选择
        workerSetsNow=getParetoFront(workerSetsNow);


        for(int i=0;i<workerSetsNow.size();i++){
            //TODO：找出一个最优解返回！！在pareto前沿（最优解集合）中直接累加所有值 找个最好的！

            Double valNow=calcVal(workerSetsNow.get(i));
            if(valNow<resNum){
                resNum=valNow;
                resID=i;
            }
        }
//        System.out.println(resID);
//        System.out.println(workerSetsNow.get(resID));
//        System.out.println(workerId);
        for(int i=0;i<workerSetsNow.get(resID).size();i++){
            if(workerSetsNow.get(resID).get(i))
                res.add(workerId.get(i));
        }
        return res;
    }


    //从集合中获取帕累托前沿
    List<List<Boolean>> getParetoFront(List<List<Boolean>> workerSetNow){
        List<Boolean> bk=new ArrayList<>();
        int len=workerSetNow.size();
        for(int i=0;i<len;i++){
            bk.add(true);//true表示没有被支配，属于pareto前沿
            for(int j=0;j<len;j++){
                if(i==j)continue;
                if(ParetoControl(workerSetNow.get(i),workerSetNow.get(j))){
                    //j支配了i
                    bk.set(i,false);
                    break;
                }
            }
        }
        List<List<Boolean>> res=new ArrayList<>();
        for(int i=0;i<len;i++){
            if(bk.get(i))res.add(workerSetNow.get(i));
        }
        //System.out.println("pareto");
        //System.out.println(res);
        return res;
    }
    //2支配了1则返回true
    boolean ParetoControl(List<Boolean> src1,List<Boolean> src2){
        Boolean res=true;
        //遍历所有条件，看是否支配
        if(calcBugDetectProb(src1)>=calcBugDetectProb(src2))
            return false;
        if(calcGroupDiversity(src1)>=calcGroupDiversity(src2))
            return false;
        if(calcDomainRelevance(src1)>=calcDomainRelevance(src2))
            return false;
        if(calcWorkerGeneralScore(src1)>=calcWorkerGeneralScore(src2))
            return false;
        if(calcWorkerNum(src1)>=calcWorkerNum(src2))
            return false;
        return res;
    }

    Double calcVal(List<Boolean> workerSet){
        //计算种群贡献 考虑策略：在都在帕雷托前沿的时候考虑归一化累加
        double tmp[]=new double[5];
        tmp[0]=calcBugDetectProb(workerSet);
        tmp[1]=calcDomainRelevance(workerSet);
        tmp[2]=calcGroupDiversity(workerSet);
        tmp[3]=Double.valueOf(calcWorkerNum(workerSet));
        tmp[4]=calcWorkerGeneralScore(workerSet);
        Double res=0.0,tot=0.0;
        for(int i=0;i<5;i++)
            tot+=tmp[i];
        for(int i=0;i<5;i++){
            res+=tmp[i]*(tot-tmp[i])/tot;//归一化
        }
        return res;
    }
    //同样采用拓扑排序
    List<Integer> getFinalOrderLevel(List<Integer> controlGroupId,List<Double> distanceIndex){
        List<Integer>res=new ArrayList<>();
        int len=controlGroupId.size();
        for(int i=0;i<len;i++){
            res.add(-1);//表示没分组
        }
        int cnt=0;//现在有多少被处理了
        //每层用queue维护 第一个数为id 第二个数为层数
        Queue<Pair<Integer,Integer>> queue = new LinkedList();
        //初始化入度
        List<Integer> inIndex=new ArrayList<>();
        List<List<Integer>> edge=new ArrayList<>();
        for(int i=0;i<len;i++){
            inIndex.add(0);
            edge.add(new ArrayList<>());
        }
        for(int i=0;i<len;i++){
            for(int j=0;j<len;j++){
                if(i==j)continue;
                //如果j优于i(支配序和拥挤度综合考虑)
                if((controlGroupId.get(j)<controlGroupId.get(i))||(controlGroupId.get(j)==controlGroupId.get(i)&&distanceIndex.get(j)>distanceIndex.get(i))){
                    int curNum=inIndex.get(i)+1;
                    inIndex.set(i,curNum);
                    List<Integer> edges=edge.get(j);
                    edges.add(i);
                    edge.set(j,edges);
                }
            }
        }
        for(int i=0;i<len;i++){
            if(inIndex.get(i)==0){
                queue.offer(new Pair(i,0));
                res.set(i,0);
            }
        }
        //拓扑排序
        while(!queue.isEmpty()){
            Pair<Integer,Integer> now=queue.poll();
            int id=now.getKey(),level=now.getValue();
            List<Integer> edges=edge.get(id);
            for(int i=0;i<edges.size();i++){
                int to=edges.get(i);
                int num=inIndex.get(to);
                if(num==1){
                    inIndex.set(to,0);
                    queue.offer(new Pair(to,level+1));
                    res.set(to,level+1);
                }
                else{
                    inIndex.set(to,num-1);
                }
            }
        }
        return res;
    }
    //两个父代遗传变异 产生两个子代
    List<List<Boolean>> calcSon(List<Boolean> fa1,List<Boolean> fa2){
        List<List<Boolean>> res=new ArrayList<>();
        List<Boolean> tmpSon0=new ArrayList<>();
        List<Boolean> tmpSon1=new ArrayList<>();
        Random rd = new Random();
        int len=fa1.size();
        //采用三个交叉互换点
        int swapPoint[]=new int[3];
        swapPoint[0]=rd.nextInt(len-1);
        swapPoint[1]=rd.nextInt(len-1);
        while(swapPoint[0]==swapPoint[1])
            swapPoint[1]=rd.nextInt(len-1);
        swapPoint[2]=rd.nextInt(len-1);
        while(swapPoint[0]==swapPoint[2]||swapPoint[1]==swapPoint[2]){
            swapPoint[2]=rd.nextInt(len-1);
        }
        for(int i=0;i<3;i++)
            for(int j=0;j<2;j++){
                if(swapPoint[j]>swapPoint[j+1]){
                    int tmp=swapPoint[j];
                    swapPoint[j]=swapPoint[j+1];
                    swapPoint[j+1]=tmp;
                }
            }
        for(int i=0;i<=3;i++){
            int posl=0;//处理区间：posl到posr
            if(i!=0)posl=swapPoint[i-1];
            int posr=len-1;
            if(i!=3)posr=swapPoint[i]-1;
            for(int j=posl;j<=posr;j++){
                if(i%2==0){
                    tmpSon0.add(fa1.get(j));
                    tmpSon1.add(fa2.get(j));
                }
                else{
                    tmpSon0.add(fa2.get(j));
                    tmpSon1.add(fa1.get(j));
                }
            }
        }
        //变异 2/len的概率
        int prob=rd.nextInt(len);
        if(prob<2){
            //变异
            int pos=rd.nextInt(len);
            tmpSon0.set(pos,!tmpSon0.get(pos));
        }
        prob=rd.nextInt(len);
        if(prob<2){
            //变异
            int pos=rd.nextInt(len);
            tmpSon1.set(pos,!tmpSon1.get(pos));
        }
        res.add(tmpSon0);
        res.add(tmpSon1);
        return res;
    }

    //TODO:NSGA遗传算法的迭代过程（一次迭代）
    List<List<Boolean>> processNSGA(List<List<Boolean>> workerSets){
        List<List<Boolean>> res=new ArrayList<>();
        //step1:通过非支配序和拥挤度计算 选择父代
        //1.1非支配排序 得出所在偏序组
        List<Integer> controlGroupId=controlSort(workerSets);
        //1.2拥挤度计算，得出每个点的拥挤度
        List<Double> distanceIndex=getDistanceIndex(workerSets);
        //1.3根据非支配序和拥挤度再次进行偏序排序
        List<Integer> finalOrderLevel=getFinalOrderLevel(controlGroupId,distanceIndex);
        //1.4按照1.3得出的偏序关系选择下一代；如果某组无法全部被选取，以拥挤度更大为选择目标

        List<List<Boolean>> nextGenerationFather=new ArrayList<>();
        int chosen=0;
        //TODO:排序后真正选择最优的50个！
        while (nextGenerationFather.size()<=groupSizeAfter){//选出进入下一代（有繁衍资格的）
            nextGenerationFather.add(workerSets.get(chosen));
            chosen++;
        }
        //System.out.println("nextGen");
        //System.out.println(nextGenerationFather);
        //step2:随机多次，随机选出两个父代，产生子代（子代规模可调整）
        List<List<Boolean>> nextGenerationSon=new ArrayList<>();
        Random rd = new Random();
        while (nextGenerationSon.size()<groupSizeAfter){
            //TODO:fa1和fa2替换成随机数
            Integer fa1=0,fa2=0;
            fa1=rd.nextInt(nextGenerationFather.size());
            fa2=rd.nextInt(nextGenerationFather.size());
            while(fa2==fa1)
                fa2=rd.nextInt(nextGenerationFather.size());
            List<List<Boolean>> sons=calcSon(nextGenerationFather.get(fa1),nextGenerationFather.get(fa2));
            //一次交配产生两个孩子 容量允许时全部加入下一代
            if(nextGenerationSon.size()<groupSizeAfter){
                if(calcWorkerNum(sons.get(0))!=0)
                    nextGenerationSon.add(sons.get(0));
            }
            if(nextGenerationSon.size()<groupSizeAfter){
                if(calcWorkerNum(sons.get(0))!=0)
                    nextGenerationSon.add(sons.get(1));
            }
        }
        //step3:父代子代合并 返回准备下一次迭代
        for (int i=0;i<nextGenerationFather.size();i++)
            res.add(nextGenerationFather.get(i));
        for(int i=0;i<nextGenerationSon.size();i++)
            res.add(nextGenerationSon.get(i));
        //System.out.println(res);
        return res;
    }
    //在某特定指标上计算拥挤度
    List<Double> calcDistanceIndex(List<Pair<Integer,Double>> idAndValue){
        List<Double>res=new ArrayList<>();
        int len=idAndValue.size();
        for(int i=0;i<len;i++)
            res.add(0.0);
        Double div=idAndValue.get(len-1).getValue()-idAndValue.get(0).getValue();
        res.set(idAndValue.get(0).getKey(),Double.MAX_VALUE/(len+1));
        res.set(idAndValue.get(len-1).getKey(),Double.MAX_VALUE/(len+1));
        for(int i=1;i<len-1;i++){
            //计算两头之外的拥挤度
            Double val=idAndValue.get(i+1).getValue()-idAndValue.get(i-1).getValue();
            int index=idAndValue.get(i).getKey();
            res.set(index,val/div);
        }
        return res;
    }

    /**
     *一个综合考量活跃度的数值
     */
    Integer getActiveContribute(Integer workerid){
        Date endDTime = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String endTime = sdf.format(endDTime);
        String startTime = MyDateUtil.getDayAgoOrAfterString(-119);
        List<Activity> activityMap = activityMapper.selectByUserId(workerid);
        List<String> dateList = MyDateUtil.getDayBetweenDates(startTime, endDTime);
        Map<String, Object> dateMap = new HashMap<>();
        for(Activity item : activityMap) {
            dateMap.put(item.getLoginDate(), item.getActivityCount());
        }
        int sum=0;
        List<List<Object>> resultList = new ArrayList<>();
        for(String item : dateList) {
            Integer count = 0;
            if(dateMap.get(item) != null) {
                count = Integer.valueOf(dateMap.get(item).toString());
            }
            List<Object> objectList = new ArrayList<>();
            objectList.add(item);
            objectList.add(count);
            sum=sum+count;
            resultList.add(objectList);
        }
        return sum;
    }

    /**
     * 降低下面几个方法的计算复杂度，先在最初进行一些预处理计算。
     */
    void initIndexs(List<User> workers){
        String task="";
        for(String word:taskDomain){
            task+=word;
        }
        hashTask = new MySimHash(task, 64);
        hashWorker=new ArrayList<>();
        activeness=new ArrayList<>();
        followers=new ArrayList<>();
        commentDup=new ArrayList<>();
        likes=new ArrayList<>();
        for(User worker:workers){
            Double res=0.0;
            String now="";
            List<String> domains=workerDomains.get(worker.getId());
            for (String word:domains){
                now+=word;
            }
            //System.out.println(now);
            MySimHash hash2 = new MySimHash(now, 64);
            hashWorker.add(hash2);

            Integer workerId=worker.getId();
            //TODO:归一化

            activeness.add(Double.valueOf(getActiveContribute(workerId)));
            followers.add(Double.valueOf(getFollowers(workerId)));
            //reportDup.add(getReportDup(workerId));
            commentDup.add(getCommentDup(workerId));
            likes.add(getLikes(workerId));
        }
        initDomainRelevance(workers);
        initBugDetectProb(workers);
        initWorkerGeneralScore(workers);
        initGroupDiversity(workers);
    }
    Double getFollowers(Integer workerId){
        Double res=0.0;
        res= Double.valueOf(followMapper.selectBySecondId(workerId).size());
//        System.out.println("follow");
//        System.out.println(res);
        return res;
    }
    Double getCommentDup(Integer workerId){
        Double res=0.0;
        res=getPlagiarismScore(workerId);
        return res;
    }
    Double getLikes(Integer workerId){
        Double res=0.0;
        List<Score> scores=scoreMapper.selectByWorker(workerId);
        for(Score score:scores){
            res+=likeMapper.countByScoreId(score.getId());
        }
        return res;
    }
    void initDomainRelevance(List<User> workers){
        for(int i=0;i<workers.size();i++){
            MySimHash hash2 = hashWorker.get(i);
            Double res=hashTask.getSemblance(hash2);
            workerRelevance.add(res);
        }
    }
    void initBugDetectProb(List<User> workers){
        bossScoresAndGeneralScores=new ArrayList<>();
        Integer num=0;
        HashMap<String,Double> mp=new HashMap<>();
        mp.put("低",1.0);
        mp.put("偏低",2.0);
        mp.put("中",3.0);
        mp.put("偏高",4.0);
        mp.put("高",5.0);
        for(User worker:workers){
            List<Double>now=new ArrayList<>();
            //TODO:计算真正找到bug的概率，用PCA
            List<Report> reports=reportMapper.selectByUser(worker.getId());
            num=0;
            for(Report report:reports){
                num=num+1;
                BossScore bossScores=bossScoreMapper.selectByReport(report.getId());
                if(bossScores==null){
                    for(int i=0;i<5;i++)
                        now.add(3.0);
                    continue;
                }
                if(num==1){
                    //System.out.println(bossScores);
                    now.add(mp.get(bossScores.getDefectImportance()));
                    now.add(mp.get(bossScores.getReality()));
                    now.add(mp.get(bossScores.getRecurrence()));
                    now.add(mp.get(bossScores.getTaskFit()));
                    now.add(mp.get(bossScores.getTotalQuality()));
                }
                else{
                    now.set(0,now.get(0)+mp.get(bossScores.getDefectImportance()));
                    now.set(1,now.get(1)+mp.get(bossScores.getReality()));
                    now.set(2,now.get(2)+mp.get(bossScores.getRecurrence()));
                    now.set(3,now.get(3)+mp.get(bossScores.getTaskFit()));
                    now.set(4,now.get(4)+mp.get(bossScores.getTotalQuality()));
                }
            }
            for(int i=0;i<now.size();i++){
                now.set(i,now.get(i)/num);
            }
            bossScoresAndGeneralScores.add(now);
        }
        double[][] data=new double[5][workers.size()];
        for(int i=0;i<workers.size();i++){
            if(bossScoresAndGeneralScores.get(i).size()!=0){
                for(int j=0;j<5;j++)
                    data[j][i]=bossScoresAndGeneralScores.get(i).get(j);
            }
            else{
                for(int j=0;j<5;j++)
                    data[j][i]=0.0;
            }

        }
        //TODO:放值 debug
        RealMatrix realMatrix = MatrixUtils.createRealMatrix(data);
        PCA bugPca=new PCA(realMatrix, PCA.CovarianceType.COVARIANCE,1);
        double[][] res=bugPca.mPrincipalComponents.getData();
        for(int i=0;i<res.length;i++)
            bugDetectChance.add(Math.abs(res[i][0]));
        //System.out.println(bugDetectChance);
    }
    void initWorkerGeneralScore(List<User> workers){
        int len=workers.size();
        double[][] data=new double[3][len];
        for(int i=0;i<len;i++){
            //System.out.println(followers);
            data[0][i]=followers.get(i);
            data[1][i]=commentDup.get(i);
            data[2][i]=likes.get(i);
        }
        RealMatrix realMatrix = MatrixUtils.createRealMatrix(data);
        PCA bugPca=new PCA(realMatrix, PCA.CovarianceType.COVARIANCE,1);
        double[][] res=bugPca.mPrincipalComponents.getData();
//        System.out.println(realMatrix);
//        System.out.println(bugPca.mPrincipalComponents);
        for(int i=0;i<res.length;i++)
            workerScore.add(Math.abs(res[i][0]));

    }
    void initGroupDiversity(List<User> workers){
        workerDiversity=new ArrayList<>();
        for(int i=0;i<workers.size();i++){
            List<Double> now=new ArrayList<>();
            for(int j=0;j<workers.size();j++){
                if(j==i){
                    now.add(0.0);
                }
                else if(j<i){
                    now.add(workerDiversity.get(j).get(i));
                }
                else {
                    MySimHash hash1=hashWorker.get(i);
                    MySimHash hash2=hashWorker.get(j);
                    Double res=1.0-hash1.getSemblance(hash2);
                    now.add(res);
                }
            }
            workerDiversity.add(now);
        }
    }
    /**
     * 以下几个方法均为对一个染色体（工人组）在各个方面下计算其函数值
     * 基本信息都预处理好了
     * 包括组大小，bug检测概率，领域相关，多样性等等
     * @param workerSet
     * @return
     */
    Integer calcWorkerNum(List<Boolean> workerSet){
        Integer res=0;
        for(int i=0;i<workerSet.size();i++){
            if(workerSet.get(i))res++;
        }
        return res;
    }

    Double calcBugDetectProb(List<Boolean> workerSet){
        Double res=0.0;
        for(int i=0;i<workerSet.size();i++){
            if(workerSet.get(i)){
                res+=bugDetectChance.get(i);
            }
        }
        return res;
    }
    Double calcDomainRelevance(List<Boolean> workerSet){
        Double res=0.0;
        for(int i=0;i<workerSet.size();i++){
            if(workerSet.get(i)){
                res+=workerRelevance.get(i);
            }
        }
        return res;
    }
    Double calcWorkerGeneralScore(List<Boolean> workerSet){
        Double res=0.0;
        for(int i=0;i<workerSet.size();i++){
            if(workerSet.get(i)){
                res+=workerScore.get(i);
            }
        }
        return res;
    }
    Double calcGroupDiversity(List<Boolean> workerSet){
        Double res=0.0;
        for(int i=0;i<workerSet.size()-1;i++)
            for(int j=i+1;j<workerSet.size();j++){
                if(workerSet.get(i)&&workerSet.get(j))
                    res+=workerDiversity.get(i).get(j);
            }
        return res;
    }

    //计算拥挤度 根据公式
    List<Double> getDistanceIndex(List<List<Boolean>> workerSets){
        int len=workerSets.size();
        List<Double>res=new ArrayList<>();
        for(int i=0;i<len;i++)
            res.add(0.0);
        //key对应是哪个点（会排序打乱）value对应值 用于传参
        List<Pair<Integer,Double>> idAndValue=new ArrayList<>();

        //TODO:排序！！！因为前四个指标都是最大化任务，把第五个指标也变成最大化，然后直接把优化目标改成最大化
        //指标1：bug detect chance（TODO：用ly jyg zzj算出来的指标+报告评分逻辑回归，注意不要和3重复）
        for(int i=0;i<len;i++){

            int tot=workerSets.get(i).size();
            Double arg=1.0*tot-1.0*calcWorkerNum(workerSets.get(i));
            idAndValue.add(new Pair(i,arg));
        }
        Collections.sort(idAndValue,(a,b)-> (int) (a.getValue()-b.getValue()));
        List<Double> tmp=calcDistanceIndex(idAndValue);
        for(int i=0;i<tmp.size();i++)
            res.set(i,res.get(i)+tmp.get(i));
        //指标2：工人和任务最大化相关性
        for(int i=0;i<len;i++){
            int tot=workerSets.get(i).size();
            Double arg=1.0*tot-1.0*calcWorkerNum(workerSets.get(i));
            idAndValue.set(i,new Pair(i,arg));
        }
        Collections.sort(idAndValue,(a,b)-> (int) (a.getValue()-b.getValue()));
        tmp=calcDistanceIndex(idAndValue);
        for(int i=0;i<tmp.size();i++)
            res.set(i,res.get(i)+tmp.get(i));
        //指标3：工人评分最大化（TODO：用ly jyg zzj算出来的指标+报告评分综合考量）
        for(int i=0;i<len;i++){
            int tot=workerSets.get(i).size();
            Double arg=1.0*tot-1.0*calcWorkerNum(workerSets.get(i));
            idAndValue.set(i,new Pair(i,arg));
        }
        Collections.sort(idAndValue,(a,b)-> (int) (a.getValue()-b.getValue()));
        tmp=calcDistanceIndex(idAndValue);
        for(int i=0;i<tmp.size();i++)
            res.set(i,res.get(i)+tmp.get(i));
        //指标4：工人差异最大化（文本分析）
        for(int i=0;i<len;i++){
            int tot=workerSets.get(i).size();
            Double arg=1.0*tot-1.0*calcWorkerNum(workerSets.get(i));
            idAndValue.set(i,new Pair(i,arg));
        }
        Collections.sort(idAndValue,(a,b)-> (int) (a.getValue()-b.getValue()));
        tmp=calcDistanceIndex(idAndValue);
        for(int i=0;i<tmp.size();i++)
            res.set(i,res.get(i)+tmp.get(i));
        //指标5：工人人数最小化->未选择工人人数最大化
        for(int i=0;i<len;i++){
            int tot=workerSets.get(i).size();
            Double arg=1.0*tot-1.0*calcWorkerNum(workerSets.get(i));
            idAndValue.set(i,new Pair(i,arg));
        }
        Collections.sort(idAndValue,(a,b)-> (int) (a.getValue()-b.getValue()));
        tmp=calcDistanceIndex(idAndValue);
        for(int i=0;i<tmp.size();i++)
            res.set(i,res.get(i)+tmp.get(i));
        return res;
    }

    //非支配排序 采用拓扑排序实现
    List<Integer> controlSort(List<List<Boolean>> workerSets){
        List<Integer>res=new ArrayList<>();
        int len=workerSets.size();
        for(int i=0;i<len;i++){
            res.add(-1);//表示没分组
        }
        int cnt=0;//现在有多少被处理了
        //每层用queue维护 第一个数为id 第二个数为层数
        Queue<Pair<Integer,Integer>> queue = new LinkedList();
        //初始化入度
        List<Integer> inIndex=new ArrayList<>();
        List<List<Integer>> edge=new ArrayList<>();
        for(int i=0;i<len;i++){
            inIndex.add(0);
            edge.add(new ArrayList<>());
        }
        for(int i=0;i<len;i++){
            for(int j=0;j<len;j++){
                if(i==j)continue;
                if(ParetoControl(workerSets.get(i),workerSets.get(j))){
                    int curNum=inIndex.get(i)+1;
                    inIndex.set(i,curNum);
                    List<Integer> edges=edge.get(j);
                    edges.add(i);
                    edge.set(j,edges);
                }
            }
        }
        for(int i=0;i<len;i++){
            if(inIndex.get(i)==0){
                queue.offer(new Pair(i,0));
                res.set(i,0);
            }
        }
        //拓扑排序
        while(!queue.isEmpty()){
            Pair<Integer,Integer> now=queue.poll();
            int id=now.getKey(),level=now.getValue();
            List<Integer> edges=edge.get(id);
            for(int i=0;i<edges.size();i++){
                int to=edges.get(i);
                int num=inIndex.get(to);
                if(num==1){
                    inIndex.set(to,0);
                    queue.offer(new Pair(to,level+1));
                    res.set(to,level+1);
                }
                else{
                    inIndex.set(to,num-1);
                }
            }
        }
        return res;
    }

    public static WorkerGroupRecommend getInstance(){
        if(instance==null){
            instance=new WorkerGroupRecommend();
        }
        return instance;
    }


    //TODO:逻辑回归
    double sigmoid(double src) {
        return 1.0 / ( 1 + Math.exp(-src));
    }

    //从工人已提交报告中获取其领域知识
    List<String> getWorkerReportDomainKnowledge(Integer userId){
        List<Report> reports=reportMapper.selectByUser(userId);
        List<String> res=new ArrayList<>();
        List<Integer> allDf=new ArrayList<>();
        List<Pair<String,Integer>> wordAndDf=new ArrayList<>();
        String src="";
        for(Report report:reports){
            src+=report.getDescription();
            src+=report.getSteps();
        }
        //System.err.println(src);
        List<Term> words= HanLP.segment(src);
        //System.err.println(words);
        List<Task> tasks=taskMapper.selectAll();
        HashMap<String,Boolean> mp=new HashMap<>();
        for(Term tm:words){
            if(!tm.nature.equals(Nature.v)&&!tm.nature.equals(Nature.ad)&&!tm.nature.equals(Nature.a)&&!tm.nature.equals(Nature.n)){
                continue;
            }
            if(tm.word.equals("是")||tm.word.equals("的")||tm.word.equals("了")||tm.nature.equals(Nature.w)){
                continue;
            }
            String word=tm.word;
            //if(mp.containsKey(word))continue;
            //mp.put(word,true);
            int df=0;
//            for(Task task:tasks){
//                //System.out.println(tm);
//                //System.out.println(task.getId());
//                TFIDFSimilarity tfidfSimilarity=tfidfSimilarityMapper.selectByWordAndTask(word,task.getId());
//                if(tfidfSimilarity!=null)
//                    df+=tfidfSimilarity.getFrequency();
//            }
            wordAndDf.add(new Pair(word,df));
            allDf.add(df);
        }
        //过滤df太低太高的部分;
        Collections.sort(wordAndDf,(a,b)->a.getValue()-b.getValue());
        int ignorel= (int) Math.floor(allDf.size()*0.05);
        int ignorer= (int) Math.floor(allDf.size()*0.95);
        for(int i=ignorel;i<ignorer;i++)
            res.add(wordAndDf.get(i).getKey());
        return res;
    }

    //从工人已提交报告中获取其领域知识
    List<String> getTaskDomainKnowledge(Integer taskId){
        Task task=taskMapper.selectByPrimaryKey(taskId);
        List<String> domainWords=new ArrayList<>();
        String src=task.getTaskName()+task.getDescription()+task.getEnvironment();

        List<Term> words= HanLP.segment(src);
        HashMap<String,Boolean> mp=new HashMap<>();
        for(Term tm:words){
//            if(!tm.nature.equals(Nature.v)&&!tm.nature.equals(Nature.ad)&&!tm.nature.equals(Nature.a)&&!tm.nature.equals(Nature.n)){
//                continue;
//            }
              if(tm.word.equals("是")||tm.word.equals("的")||tm.word.equals("了")){
                continue;
              }
            String word=tm.word;
            if(mp.containsKey(word))continue;
            mp.put(word,true);
            domainWords.add(word);
        }
        return domainWords;
    }
    //评价的重复度
    public Double getPlagiarismScore(Integer userId){
        List<Score> scores = scoreMapper.selectByWorker(userId);
        if(scores == null)
            return 0.0;
        System.out.println(scores.size());
        Double plagiarism = 0.0;
        Integer num = 0;
        for(Score score: scores){
            Integer firstId = score.getId();
            List<ScoreSimilarity> similarities = scoreSimilarityMapper.selectByFirstId(firstId);
            for(ScoreSimilarity scoreSimilarity: similarities){
                System.out.println(scoreSimilarity.getSimilarity());
                plagiarism += scoreSimilarity.getSimilarity();
                num ++;
            }
        }
        if(num == 0)
            return 0.0;
        return plagiarism / num.doubleValue();
    }


//    double getCooperationAbility(Integer userId){
//        //排除是非worker的情况
//        User user = userMapper.selectByPrimaryKey(userId);
//        CoordinationAbility coordinationAbility = coordinationAbilityMapper.selectByUserId(userId);
//        if(coordinationAbility==null)
//            return 0.6;
//        Integer reportNum = coordinationAbility.getReportNum();
//        CoordinationAbilityVO coordinationAbilityVO = new CoordinationAbilityVO();
//        Double badRate = 0.0;
//        Integer badNum = coordinationAbility.getBadReportNum();
//        if(badNum != 0)
//            badRate = coordinationAbility.getModifiedReportNum().doubleValue() / coordinationAbility.getBadReportNum().doubleValue();
//        //六四开计算，num占到五分里面的三分，badRate占到五分里面的两分
//        //number>=1, badRate在0-1之间，要差不多统一到5
//        Double enthusiasm = reportNum.doubleValue() / 0.3 + badRate * 2;
//        Double goodRate = (reportNum.doubleValue() - badNum.doubleValue()) / reportNum.doubleValue();
//        //查找用户的ranking
//        List<CoordinationRanking> rankings = coordinationRankingMapper.selectAll();
//        Integer workerNum = rankings.size();
//        Integer index = 0;
//        List<WorkerSimilarityVO> workerSimilarities = new ArrayList<>();
//        for(int i = 0; i < workerNum; i ++){
//            CoordinationRanking coordinationRanking = rankings.get(i);
//            if(coordinationRanking.getUserId().equals(userId)){
//                index = i + 1;
//            }
//            workerSimilarities.add(new WorkerSimilarityVO(coordinationRanking.getUserId(),coordinationRanking.getTotalSimilarity()));
//        }
//        Double rank = index.doubleValue() / workerNum.doubleValue();
//        coordinationAbilityVO.setRank(rank);
//        coordinationAbilityVO.setWorkerSimilarities(workerSimilarities);
//        Double generalScore = 0.0;
//        //根据积极性（0-5），优质报告率，独特性排名算分数，总分0-1
//        //比例是3，4，3
//        generalScore = enthusiasm / 5 * 0.3 + goodRate * 0.4 + (1 - rank) * 0.5;
//        if(generalScore>1)
//            generalScore=1.0;
//        return generalScore;
//        //return Math.random();
//    }
    double getCooperationAbility(Integer userId) {
        //排除是非worker的情况
        User user = userMapper.selectByPrimaryKey(userId);
        CoordinationAbility coordinationAbility = coordinationAbilityMapper.selectByUserId(userId);
        if(coordinationAbility == null)
            return 0.0;
        Integer reportNum = coordinationAbility.getReportNum();
        //报告数目不足3个，这个时候应该不显示相似度信息
        if(reportNum < 1)
            return 0.0;
        CoordinationAbilityVO coordinationAbilityVO = new CoordinationAbilityVO();
        Double badRate = 0.0;
        Integer badNum = coordinationAbility.getBadReportNum();
        if(badNum == 0)   //没有错误报告，肯定该认为是1
            badRate = 1.0;
        if(badNum != 0)
            badRate = coordinationAbility.getModifiedReportNum().doubleValue() / coordinationAbility.getBadReportNum().doubleValue();
        //六四开计算，num占到五分里面的三分，badRate占到五分里面的两分
        //number>=1, badRate在0-1之间，要差不多统一到5
        Double enthusiasm = reportNum.doubleValue() * 0.3 + badRate * 2;
        Double goodRate = (reportNum.doubleValue() - badNum.doubleValue()) / reportNum.doubleValue();
        coordinationAbilityVO.setUserId(userId);
        coordinationAbilityVO.setCoordinateNum(reportNum);
        coordinationAbilityVO.setBadReportModifyRate(badRate);
        coordinationAbilityVO.setEnthusiasm(enthusiasm);
        coordinationAbilityVO.setGoodReportRate(goodRate);
        //获取一个用户对应的所有领域的报告情况,可以考虑连接查询，把所有连接后的对象返回回来
        //这样既可以计算每一个领域的得分，也可以计算平均文本相似度和图片相似度
        List<ReportTask> reportTasks = reportMapper.selectReportTaskByUserid(userId); //拿到了带有报告和task信息的po
        Integer totalNum = 0;   //总报告数目木
        List<ReportSimilarityVO> reportSimilarities = new ArrayList<>();
        //下标1，2，3分别对应功能测试，性能测试，bug探索
        Double[] fieldSims = new Double[]{0.0,0.0,0.0,0.0};
        Integer[] fieldNums = new Integer[]{0,0,0,0};
        Double avgTextSim = 0.0;
        Double avgPicSim = 0.0;
        HashMap<String, Integer> field = new HashMap<String, Integer>();
        field.put("function_test",1);
        field.put("performance_test",2);
        field.put("bug_explore",3);
        for(ReportTask reportTask: reportTasks){
            //说明这个报告的相似度还没算完
            if(reportTask.getTextSimilarity() == null || reportTask.getPicSimilarity() == null)
                continue;
            totalNum++;
            avgTextSim += reportTask.getTextSimilarity();
            avgPicSim += reportTask.getPicSimilarity();
            //判断属于哪一个领域，并对对应领域赋值
            Integer type = field.get(reportTask.getTaskType());
            fieldNums[type] ++;
            fieldSims[type] += (reportTask.getTextSimilarity() + reportTask.getPicSimilarity()) / 2;
            //加入reportSimilarities中
            reportSimilarities.add(new ReportSimilarityVO(reportTask.getId(),reportTask.getTextSimilarity(),reportTask.getPicSimilarity()));
        }
        //计算总报告相似度
        avgTextSim /= totalNum;
        avgPicSim /= totalNum;
        coordinationAbilityVO.setAvgTextSim(avgTextSim);
        coordinationAbilityVO.setAvgPicSim(avgPicSim);
        coordinationAbilityVO.setSimList(reportSimilarities);
        //按照相似度6，报告数量4计算擅长的领域，使用fieldSims来保存。注意报告数量要现*0.1保证在同一个量级上
        //fieldNums相似度越高说明越不好，因此要考虑用1减去
        for(int i = 1; i <= 3; i ++){
            //首先计算平均相似度
            if(fieldNums[i] == 0)
                //说明这个field是0，跳过就行了
                continue;
            fieldSims[i] = fieldSims[i] / fieldNums[i];
            System.out.println(fieldSims[i]);
            fieldSims[i] = (1-fieldSims[i]) * 6 + fieldNums[i] * 0.4;
        }
        //判断哪个领域数值最大
        int type = -1;
        double max = -1.0;
        for(int i = 1; i <= 3; i ++){
            if(fieldSims[i] > max){
                max = fieldSims[i];
                type = i;
            }
        }
        String key = "";
        //获取对应的领域string
        for(String getkey: field.keySet()){
            if(field.get(getkey).equals(type)){
                key = getkey;
                break;
            }
        }
        coordinationAbilityVO.setSkill(key);
        //查找用户的ranking
        List<CoordinationRanking> rankings = coordinationRankingMapper.selectAll();
        Integer userRank = 0;
        int idx = 0;
        List<WorkerSimilarityVO> workerSimilarities = new ArrayList<>();
        for (CoordinationRanking coordinationRanking : rankings) {
            idx++;
            if (coordinationRanking.getUserId().equals(userId))
                userRank = idx;
            workerSimilarities.add(new WorkerSimilarityVO(coordinationRanking.getUserId(), coordinationRanking.getTotalSimilarity()));
        }
        System.out.println("userRank:" + userRank);
        Double rank = userRank.doubleValue() / rankings.size();
        coordinationAbilityVO.setRank(rank);
        coordinationAbilityVO.setWorkerSimilarities(workerSimilarities);
        Double generalScore = 0.0;
        //根据积极性（0-5），优质报告率，独特性排名算分数，总分0-1
        //比例是3，4，3
        generalScore = enthusiasm / 5 * 0.3 + goodRate * 0.4 + (1 - rank) * 0.5;
        coordinationAbilityVO.setScore(generalScore);
        return coordinationAbilityVO.getScore();
    }

    double getReviewAbility(Integer userId){
        Double res=-1.0;
        Double mx=0.0;
        for(int i=0;i<workerId.size();i++){
            if(workerId.get(i)==userId){
                res=workerScore.get(i);
            }
            if(workerScore.get(i)>mx)mx=workerScore.get(i);
        }
        if(mx==0.0||res==-1.0)
            return 3.0;
        return res/mx*5;
    }
    double getOthersEvaluation(Integer userId){
        Double res=0.0;
//        int tot=0;
//        for(int i=0;i<bossScoresAndGeneralScores.size();i++){
//            List<Double> list=bossScoresAndGeneralScores.get(i);
//            for(int j=0;j<list.size();j++){
//                tot++;
//                res+=list.get(j);
//            }
//        }
//        if(tot==0)return 3.0;
//        else return res/tot;
        HashMap<String,Double> mp=new HashMap<>();
        mp.put("低",1.0);
        mp.put("偏低",2.0);
        mp.put("中",3.0);
        mp.put("偏高",4.0);
        mp.put("高",5.0);
        List<Double>now=new ArrayList<>();
        //TODO:计算真正找到bug的概率，用PCA
        List<Report> reports=reportMapper.selectByUser(userId);
        int num=0;
        for(Report report:reports){
            BossScore bossScores=bossScoreMapper.selectByReport(report.getId());
            if(bossScores==null){
                num+=5;res+=15;
                continue;
            }
            //System.out.println(bossScores);
            res+=mp.get(bossScores.getDefectImportance());
            res+=mp.get(bossScores.getReality());
            res+=mp.get(bossScores.getRecurrence());
            res+=mp.get(bossScores.getTaskFit());
            res+=mp.get(bossScores.getTotalQuality());
            num+=5;
        }
        if(num==0)return 3.0;
        else return res/num;
    }
    @Override
    public ResultVO<List<taskRecommendWorkersVO>> getRecommendWorkerGroup(Integer taskid) {
        List<Integer> workers=this.recommendExecute(taskid);
        //System.out.println(workers);
        List<taskRecommendWorkersVO> res=new ArrayList<>();
        for(Integer userId:workers){
            taskRecommendWorkersVO now=new taskRecommendWorkersVO();
            User user=userMapper.selectByPrimaryKey(userId);
            now.setCooperationAbility(getCooperationAbility(userId)*5);
            now.setReviewAbility(getReviewAbility(userId));
            now.setOthersEvaluation(getOthersEvaluation(userId));
            now.setUsername(user.getUsername());
            res.add(now);
        }
        return new ResultVO<>(Constant.REQUEST_SUCCESS,"获取推荐用户群体成功！",res);
    }
}
